Las notas de este curso introductorio están basadas en el material de los profesores Ernesto Barrios, Andreu Boada, Luis Felipe González y Teresa Ortiz. Así como en el tutorial del Hadley Wickham y Garret Grolemund “R for Data Science”.





“The simple graph has brought more information to the data analyst’s mind than any other device.” — John Tukey




The Grammar of Graphics (gg)

Se usará el paquete ggplot2 de Hadley Wickham para la visualización de datos. Este paquete usa los principios de gramática de gráficas (por esto “gg”) de Leland Wilkinson’s 1999.

library(ggplot2)
library(stringr)
library(readr)

0. ¿Cómo funciona?

Se incluye un conjunto de datos (data.frame), un conjunto de objetos geometricos geom_ en un sistema de coordenadas (x,y).


En ggplot2 se construye la gráfica agregando diferentes capas o layers, diferentes tipos de capas que se trataran en este curso son:

  1. Aesthetic: aes() propiedades de objetos por mapear en la gráfica (axis x, axis y, size, shape, color, fill).

  2. Geoms: geom_ Objetos geométricos. Estos objetos se dividen en objetos que mapean en una dimensión, dos dimensiones y tres dimensiones.

  3. Transformaciones estadísticas: stat_ Resúmen estadístico de variables.

  4. Facets: facet_ División de gráficas en diferentes paneles o subgráficas.

  5. Themes: theme() Aspectos de la gráfica independientes de los datos, como: fuente, títulos, posición de leyendas y fondo.

Existen dos funciones para graficar: ggplot y qplot. Aunque qplot es muy sencilla y similar a plot, en este curso se pondrá especial antención a la función ggplot.

Se seguirá el siguiente orden para ejemplificar distintas gráficas:

  • Gráfica de dispersión
  • Páneles
  • Distintos tipos de gráficas


1. Gráficas de dispersión

Usaremos el conjunto de datos mpg que se incluye en R, puedes encontrar información de esta base de datos tecleando ?mpg.

# estructura de la base
str(mpg)
## Classes 'tbl_df', 'tbl' and 'data.frame':    234 obs. of  11 variables:
##  $ manufacturer: chr  "audi" "audi" "audi" "audi" ...
##  $ model       : chr  "a4" "a4" "a4" "a4" ...
##  $ displ       : num  1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
##  $ year        : int  1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
##  $ cyl         : int  4 4 4 4 6 6 6 4 4 4 ...
##  $ trans       : chr  "auto(l5)" "manual(m5)" "manual(m6)" "auto(av)" ...
##  $ drv         : chr  "f" "f" "f" "f" ...
##  $ cty         : int  18 21 20 21 16 18 18 18 16 20 ...
##  $ hwy         : int  29 29 31 30 26 26 27 26 25 28 ...
##  $ fl          : chr  "p" "p" "p" "p" ...
##  $ class       : chr  "compact" "compact" "compact" "compact" ...


Para realizar una gráfica de dispersión:

  1. Se debe especificar explícitamente que base de datos usamos, este es el primer argumento en la función ggplot

  2. Dentro de aes() escribimos la variable que queremos graficar en cada eje.

  3. Posteriormente se definen las geometrías, geoms, que controlan el tipo de gráfica. Así se agrega otra capa a la gráfica con el símbolo (+).

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_point()

La forma paralela de graficar con qplot se presenta abajo.

qplot(displ, hwy, data = mpg, geom = 'point')

Podemos representar variables adicionales usando otras características estéticas (aesthetics) como forma, color o tamaño.

ggplot(mpg, aes(x = displ, y = hwy, color = class)) + 
  geom_point()

La forma paralela de graficar con qplot se presenta abajo.

qplot(displ, hwy, color = class, data = mpg, geom = 'point')

Ahora se prueba con otras variables estéticas.

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy, alpha = class))

Agregando valores estéticos podemos graficar hasta 5 variables en una sola gráfica.

mpg$trans <- str_replace( str_extract( mpg$trans, pattern = ".*[(]"), "[(]", "")
ggplot(mpg, aes(x = displ, y = hwy, 
                color = class, shape = trans,
                size = cyl)) + 
  geom_point()


Tipos de variables

El mapeo de las propiedades estéticas depende del tipo de variable, las variables discretas se mapean a distintas escalas que las variables continuas:

  Discreta Continua
Color Arcoiris de colores Gradiente de colores
Tamaño Escala discreta de tamaños Mapeo lineal entre el radio y el valor
Forma Distintas formas No aplica


En la siguiente gráfica se muestra la variable categórica class reordenada en el eje x.

ggplot(mpg, aes(x = reorder(class, hwy), y = hwy)) + 
    geom_point() 

El problema con la gráfica es que no es muy informativa, por que no se observa variabilidad de los puntos. Una forma de corregirla es agregando vibraciones o jitter.

ggplot(mpg, aes(x = reorder(class, hwy), y = hwy)) + 
  geom_point(color = 'red') + 
  geom_jitter()

Se mencionó previamente que es posible graficar resúmenes estadísticos. En este ejemplo, agregaremos una capa más con un objeto boxplot.

ggplot(mpg, aes(x = reorder(class, hwy), y = hwy)) + 
  geom_point(color = 'red') + 
  geom_jitter() + 
  geom_boxplot(color = 'blue')

Nota que cada objeto de cada capa que se agrega tiene asignado un color diferente.

Modificar elementos

La gráfica tiene diferentes elementos, como: texto de los ejes, títulos de los ejes, títulos de las gráficas, leyendas de estéticas, etc. Una forma de modificar el tamaño, color, posición o rotación es mediante la función theme() y element_text().

Por ejemplo, de la base mpg queremos observar el rendimiento de millas por galón en carretera hwy por cada fabricante manufacturer.

ggplot(mpg, aes(x = reorder(manufacturer, hwy), y = hwy)) + 
  geom_point() + 
  geom_jitter() + 
  geom_boxplot() 

El problema con la gráfica anterior es la dificultad para leer los fabricante, por lo que se hace una rotación del texto del eje x.

ggplot(mpg, aes(x = reorder(manufacturer, hwy), y = hwy)) + 
  geom_point() + 
  geom_jitter() + 
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

Dentro de cada geom es posible cambiar distintas características. Por ejemplo, el color y tamaño de cada punto de la gráfica de dispersión.

ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy), color = "blue", size = 5)

¿Por qué la siguiente gráfica no imprime los puntos en color azul?

ggplot(data = mpg) + 
  geom_point(aes(x = displ, y = hwy, color = "blue"))

¿Qué sucede si en la siguiente gráfica agregas legend.position = "none" o legend.position = "bottom".

ggplot(mpg, aes(displ, hwy)) + 
  geom_point(aes(colour = class)) + 
  theme()

¿Qué sucede si en el objeo geométrico de los puntos incluyes show.legend = F?


Ejercicio:

  1. Importa los datos “conapo.csv” y nombra el dataframe como conapo.
  2. Imprime la estructura de conapo.
  3. ¿Qué sucede si sólo graficas ggplot(data = conapo)? ¿Porqué?
  4. ¿Qué sucede si sólo graficas ggplot(data = conapo, aes(x = salarios_minimos, y = sin_electricidad))? ¿Porqué?
  5. Has una tabla de la Ciudad de México (CVE_ENT: ‘09’) únicamente y realiza una gráfica de dispersión de salarios mínimos y sin electricidad. Colorea según analfabeta. Lee la ayuda de geom_text() y agrega una capa con el nombre de cada delegación (NOM_MUN).
  6. Lee la ayuda de reorder(). Realiza una gráfica de dispersión por entidad federativa (NOM_ENT) y porcentaje de la población analfabeta vibrada ordenando por la mediana de analfabeta .
  7. Agrega a la gráfica anterior un diagrama de caja y brazos ¿Cómo harías para graficar los puntos encima de las cajas de boxplot? A los puntos vibrados agregales una transparencia de 0.2.

Respuesta:

# 1. 
library(readr)
conapo <- read_csv('data/conapo.csv')

# 2.
str(conapo)

# 3. 
ggplot(data = conapo)

# 4. 
ggplot(data = conapo, aes(x = salarios_minimos, y = sin_electricidad))

# 5. 
tab.df <- conapo[conapo$CVE_ENT == '09', ]
ggplot(tab.df, 
       aes(x = salarios_minimos, y = sin_electricidad)) + 
  geom_point( aes( color = analfabeta), size = 5) +
  geom_text(aes(label = NOM_MUN), check_overlap = T)

# 6.
ggplot(conapo, aes(x = reorder(NOM_ENT, analfabeta, median), 
                   y = analfabeta)) + 
  geom_point(size = .3) +
  geom_jitter(size = .2) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

# 7.
ggplot(conapo, aes(x = reorder(NOM_ENT, analfabeta, median), 
                   y = analfabeta)) + 
  geom_boxplot() + 
  geom_jitter(alpha = .2, size = .3) + 
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 
  ylab('Proporción de población analfabeta\nen el muncipio')+
  xlab('Entidad Federativa')



2. Gráficas de pánel

El objetivo de las gráficas de panel es hacer varios múltiplos de una gráfica, donde cada múltiplo representa un subconjunto de los datos. Es una práctica muy útil para explorar relaciones condicionales.

En ggplot una forma es usar la función facet_wrap() para hacer paneles dividiendo los datos de acuerdo a las categorías de una sola variable.

library(ggplot2)
ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_jitter() +
  facet_wrap(~ class)

También podemos hacer una cuadrícula de 2 dimensiones usando facet_grid(filas~columnas)

ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_jitter() +
  facet_grid(trans~ class)

Ejercicio:

  1. Recrea la siguiente gráfica. ¿Hay algún problema si la variable con la que generas los paneles es numérica?

  1. ¿Qué gráfica se obtiene del siguientes código? ¿Qué hace .?
ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_jitter() +
  facet_grid(.~ class)
  1. ¿Qué sucede si en el objeto de panel facet_wrap() de la siguiente gráfica agregas scales = 'free_y' y nrow = 1?
ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_jitter() +
  facet_wrap(~ class)
  1. Lee los datos ‘ingresos.csv’. Crea una variable con valor TRUE para los salarios que esten por arriba de la mediana y FALSE para los que no.
  2. Grafica el género ordenado por la mediana de ingreso contra el ingreso y divide la gráfica por la variable binaria que creaste en el inciso 4.
  3. Crea una variable de cortes de edad: menores o igual 35 años y mayores o igual a 35 años. (Tip: Usa la función cut(). Vale la pena revisar las funciones cut_interval() o cut_number())
  4. Repite la gráfica del inciso 5. pero ahora divide la gráfica por los rangos de edad que acabas de crear.

Respuesta:

# 1. 
ggplot(mpg, aes(x = displ, y = hwy)) + 
  geom_jitter() +
  facet_wrap(~ year)

# 4.
ingresos <- read_csv("data/ingresos.csv")
ingresos$ingreso_med <- ingresos$ingreso > median(ingresos$ingreso)

# 5. 
ggplot(ingresos,
       aes(x = reorder(genero, ingreso, median),
           y = ingreso)) + 
  geom_point() + 
  geom_jitter() + 
  geom_boxplot()+ 
  facet_wrap(~ingreso_med) + 
  xlab('Género') + 
  ylab('Ingreso')

# 6.
ingresos$edad.cut <- cut(ingresos$edad, breaks = c(0,35,60))

# 7. 
ggplot(ingresos,
       aes(x = reorder(genero, ingreso, median), y = ingreso)) + 
  geom_point() + 
  geom_jitter() + 
  geom_boxplot()+ 
  facet_wrap(~edad.cut)+ 
  xlab('Género') + 
  ylab('Ingreso')



3. Otras Gráficas

Suavizamientos

Al agregar suavizamientos se ajusta un modelo a los datos y en la grafica se imprimen las predicciones del modelo. La forma en que se agregan es con el objeto geométrico geom_smooth().

En la siguiente gráfica para entender con mayor facilidad la gráfica de dispersión entre radiación solar y ozono se crea una nueva variable de la velocidad del viento y se agrega un suavizador (loess) por panel.

data(airquality)
airquality$Wind.cat <- cut_number(airquality$Wind, 3)
ggplot(airquality, aes(x = Solar.R, y = Ozone)) + 
  geom_point() +
  facet_wrap(~ Wind.cat) + 
  geom_smooth(span = 3)

Ejercicio:

¿Qué sucede si en el objeto del suavizamiento de la gráfica anterior agregas los siguientes argumentos: se = F, method = "lm"?

ggplot(airquality, aes(x = Solar.R, y = Ozone)) + 
  geom_point() +
  facet_wrap(~ Wind.cat) + 
  geom_smooth()


Series

En ocasiones se desea ver en alguna secuencia las variables. Supongamos que queremos ver la tendencia de los nombres John, Michael, Joshua y Christopher para niños. Para ello se genera un subconjunto de la base de datos.

bnames <- read_csv('data/bnames2.csv')
bnames$sex <- factor(bnames$sex, levels = 1:2, labels = c('boy', 'girl'))

sub.bnames <- bnames[bnames$name %in% 
                       c("John", "Michael", "Joshua", "Christopher") & 
                       bnames$sex == 'boy',] 

ggplot(sub.bnames, aes(x = year, y = percent, 
                       color = name, group = name)) +
  geom_line(size = 1) 

En esta gráfica se presenta el valor de estética group que permite agrupar los datos por nombre name y para cada nombre crea una línea con color y tipo (linetype) único.

Ejercicio:

¿Qué sucede si en el objeto de línea geom_line() agregas la variable aes(color = name)? ¿Qué sucede si lo agregas en los valores estéticos iniciales en la función ggplot?

ggplot(sub.bnames, aes(x = year, y = percent, 
                       group = name)) +
  geom_point() + 
  geom_line()


Barras

Las gráficas de barras se generan con el objeto geom_bar(). Este es uno de los objetos que hacen transformaciones estadísticas, en este caso en particular conteos.

En la siguiente gráfica se muestra el conteo de diamantes por cada calidad de corte cut.

ggplot(data = diamonds, 
       aes(x = cut)) + 
  geom_bar()

Es posible cambiar el valor estadístico de conteos, que es el estadístico default, a los conteos proporcionales con la especificación entre ..prop.. como se muestra abajo. Observar el eje y.

ggplot(data = diamonds, 
       aes(x = cut, y = ..prop.., group = 1)) + 
  geom_bar()

Ahora, también es posible obtener la distribución proporcional de cada medida de claridad del diamante por corte cut.

ggplot(data = diamonds, 
       aes(x = cut, fill = clarity)) + 
  geom_bar( position = "fill")


Histogramas

Los histogramas muestran la distribución de una variable numérica. El objeto geométrico para crear histogramas es geom_histogram(). La forma en que funciona este objeto es cortando la variable y cuenta el número de observaciones en cada corte. La forma de controlar el ancho de los cortes es con el argumento binwidth, es recomendable jugar con diferentes anchos de corte.

conapo <- read_csv('data/conapo.csv')
ggplot(data = conapo, aes( x= sin_primaria)) + 
  geom_histogram(binwidth =  5)

También se pueden sobre poner distintas distribución dependiendo de variables categóricos.

sub <- conapo[conapo$CVE_ENT %in% c("09", "07", "30"),]
ggplot(data = sub, aes( x= sin_primaria, fill = NOM_ENT)) + 
  geom_histogram(alpha = .6, 
                 position = 'identity', 
                 binwidth = 3) 

Nota: También se recomiendan los objetos geom_density() y geom_freqpoly() muestran distribuciones de variables.


Información estadística

Los siguientes objetos resumen información estadística con distintas transformaciones. Es por esto que no son objetos geométricos, son objetos estadísticos y se llaman como stat_:

  • QQ-Plot: stat_qq Realiza una gráfica cuantil-cuantil de una muestra y la distribución teórica. Por default compara los cuantiles teóricos de la distribución normal.
tab <-  data.frame(
  simulación = rexp(300, rate = 5)
)
ggplot(tab, aes(sample = simulación)) + 
      stat_qq()

En el siguiente ejemplo se modifica para comparar con los cuantiles teóricos de la distribución exponencial.

ggplot(tab, aes(sample = simulación)) + 
      stat_qq(distribution = qexp, dparams = 5, 
              color = 'gray40') 

  • Distribución acumulada: stat_qq Este objeto realiza las transformaciones necesarias para presentar la distribución acumulada de una muestra.

En la siguiente gráfica se compara la distribución de simulaciones normales con distintos parámetros.

df <- data.frame(x = c(rnorm(100, 0, 3), 
                     rnorm(100, 0, 10)
                     ),
                 g = gl(2, 100))
ggplot(df, aes(x, colour = g)) + 
  stat_ecdf()


Funciones

Este objeto permite graficar funciones en un rango determinado. El objeto estadístico es stat_function() que tiene como argumento una función.

La siguiente gráfica muestra una función predeterminada del objeto estadístico dnorm, que es la distribución normal con media 2 y desviación 0.5.

ggplot(data.frame(x = c(-5, 7)), aes(x)) + 
  stat_function(fun = dnorm, args = list(mean = 2, sd = .5))

También es posible gráficar cualquier función, como en el siguiente ejemplo:

FunSin <- function(x){
  sin(x)^2
}
ggplot(data.frame(x = c(-5, 5)), aes(x)) + 
  stat_function(fun = FunSin, size = 1, color = 'salmon')


Ejercicio:

  1. Recrea el código de R para generar las siguientes gráficas.
## `geom_smooth()` using method = 'loess'

## `geom_smooth()` using method = 'loess'

## `geom_smooth()` using method = 'loess'

  1. Importa la base bnames2.csv y recrea la siguiente gráfica. Únicamente considera los nombres de niña que inician con Em. (Tip: usa la función str_detect())

Respuesta:

# 1a.
ggplot(mpg, aes(displ, hwy)) + 
  geom_point() + 
  geom_smooth() + 
  ggtitle('1a')

# 1b.
ggplot(mpg, aes(displ, hwy, group = drv)) + 
  geom_point() +
  geom_smooth(se = F) + 
  ggtitle('1b')

# 1c.
ggplot(mpg, aes(displ, hwy)) + 
  geom_point(aes(colour = drv)) +
  geom_smooth() + 
  ggtitle('1c')

# 1d.
ggplot(mpg, aes(displ, hwy, color = drv)) + 
  geom_point() +
  geom_smooth(method = 'lm', aes(linetype = drv))+ 
  ggtitle('1d')

# 2.
bnames <- read_csv('data/bnames2.csv')
bnames$sex <- factor(bnames$sex, levels = 1:2, labels = c('boy', 'girl'))
ggplot( bnames[ bnames$sex == 'girl' & str_detect(bnames$name, "Em"), ],
        aes(x = year, y = percent, group = name)) +
  geom_line()  + 
  facet_wrap(~name, scales = 'free_y')


4. Resumen de objetos


5. Referencias